package com.ruoyi.filemanager.command;

import com.ruoyi.filemanager.constant.ElFinderConstants;
import com.ruoyi.filemanager.core.ElfinderContext;
import com.ruoyi.filemanager.core.Target;
import com.ruoyi.filemanager.service.ElfinderStorage;
import com.ruoyi.filemanager.service.VolumeHandler;
import com.ruoyi.filemanager.support.archiver.ArchiverOption;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public abstract class AbstractCommand implements ElfinderCommand
{
    private static final String CMD_TMB_TARGET = "?cmd=tmb&target=%s";
    // visible to subclasses
    protected final Logger logger = LoggerFactory.getLogger(getClass());
    private Map<String, Object> options = new HashMap<>();

    protected void addChildren(Map<String, VolumeHandler> map, VolumeHandler target) throws IOException
    {
        for (VolumeHandler f : target.listChildren())
        {
            map.put(f.getHash(), f);
        }
    }

    protected void addSubFolders(Map<String, VolumeHandler> map, VolumeHandler target) throws IOException
    {
        for (VolumeHandler f : target.listChildren())
        {
            if (f.isFolder())
            {
                map.put(f.getHash(), f);
                // addSubFolders(map, f);
            }
        }
    }

    protected void createAndCopy(VolumeHandler src, VolumeHandler dst) throws IOException
    {
        if (src.isFolder())
        {
            createAndCopyFolder(src, dst);
        }
        else
        {
            createAndCopyFile(src, dst);
        }
    }

    private void createAndCopyFile(VolumeHandler src, VolumeHandler dst) throws IOException
    {
        dst.createFile();
        InputStream is = src.openInputStream();
        OutputStream os = dst.openOutputStream();
        IOUtils.copy(is, os);
        is.close();
        os.close();
    }

    private void createAndCopyFolder(VolumeHandler src, VolumeHandler dst) throws IOException
    {
        dst.createFolder();

        for (VolumeHandler c : src.listChildren())
        {
            if (c.isFolder())
            {
                createAndCopyFolder(c, new VolumeHandler(dst, c.getName()));
            }
            else
            {
                createAndCopyFile(c, new VolumeHandler(dst, c.getName()));
            }
        }
    }

    @Override
    public void execute(ElfinderContext context) throws Exception
    {
        ElfinderStorage elfinderStorage = context.getVolumeSourceFactory().getVolumeSource();
        execute(elfinderStorage, context.getRequest(), context.getResponse());
    }

    public abstract void execute(ElfinderStorage elfinderStorage, HttpServletRequest request,
            HttpServletResponse response) throws Exception;

    protected Object[] buildJsonFilesArray(HttpServletRequest request, Collection<VolumeHandler> list)
    {

        ExecutorService executor = Executors.newCachedThreadPool();
        CountDownLatch latch = new CountDownLatch(list.size());
        List<Map<String, Object>> jsonFileList = new ArrayList<>();

        for (VolumeHandler itemHandler : list)
        {
            executor.execute(new Task(jsonFileList, request, itemHandler, latch));
        }

        try
        {
            latch.await();
            executor.shutdown();
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }

        return jsonFileList.toArray();
    }

    protected VolumeHandler findCwd(ElfinderStorage elfinderStorage, String target)
    {
        VolumeHandler cwd = null;
        if (target != null)
        {
            cwd = findTarget(elfinderStorage, target);
        }

        if (cwd == null)
            cwd = new VolumeHandler(elfinderStorage.getVolumes().get(0).getRoot(), elfinderStorage);

        return cwd;
    }

    protected VolumeHandler findTarget(ElfinderStorage elfinderStorage, String hash)
    {
        Target target = elfinderStorage.fromHash(hash);
        if (target == null)
        {
            return null;
        }
        return new VolumeHandler(target, elfinderStorage);
    }

    protected List<Target> findTargets(ElfinderStorage elfinderStorage, String[] targetHashes)
    {
        if (elfinderStorage != null && targetHashes != null)
        {
            List<Target> targets = new ArrayList<>(targetHashes.length);
            for (String targetHash : targetHashes)
            {
                Target target = elfinderStorage.fromHash(targetHash);
                if (target != null)
                    targets.add(target);
            }
            return targets;
        }
        return Collections.emptyList();
    }

    protected Map<String, Object> getTargetInfo(final HttpServletRequest request, final VolumeHandler target)
            throws IOException
    {
        Map<String, Object> info = new HashMap<>();

        info.put(ElFinderConstants.ELFINDER_PARAMETER_HASH, target.getHash());
        info.put(ElFinderConstants.ELFINDER_PARAMETER_MIME, target.getMimeType());
        info.put(ElFinderConstants.ELFINDER_PARAMETER_TIMESTAMP, target.getLastModified());
        info.put(ElFinderConstants.ELFINDER_PARAMETER_SIZE, target.getSize());
        info.put(ElFinderConstants.ELFINDER_PARAMETER_READ, target.isReadable()
                ? ElFinderConstants.ELFINDER_TRUE_RESPONSE : ElFinderConstants.ELFINDER_FALSE_RESPONSE);
        info.put(ElFinderConstants.ELFINDER_PARAMETER_WRITE, target.isWritable()
                ? ElFinderConstants.ELFINDER_TRUE_RESPONSE : ElFinderConstants.ELFINDER_FALSE_RESPONSE);
        info.put(ElFinderConstants.ELFINDER_PARAMETER_LOCKED, target.isLocked()
                ? ElFinderConstants.ELFINDER_TRUE_RESPONSE : ElFinderConstants.ELFINDER_FALSE_RESPONSE);

        if (target.getMimeType() != null && target.getMimeType().startsWith("image"))
        {
            StringBuffer qs = request.getRequestURL();
            info.put(ElFinderConstants.ELFINDER_PARAMETER_THUMBNAIL,
                    qs.append(String.format(CMD_TMB_TARGET, target.getHash())));
        }
        else if (target.getMimeType() != null && target.getMimeType().contains("zip"))
        {
            info.put(ElFinderConstants.ELFINDER_PARAMETER_ARCHIVERS_KEY, target.getHash());
        }

        if (target.isRoot())
        {
            info.put(ElFinderConstants.ELFINDER_PARAMETER_DIRECTORY_FILE_NAME, target.getVolumeAlias());
            info.put(ElFinderConstants.ELFINDER_PARAMETER_VOLUME_ID, target.getVolumeId());
        }
        else
        {
            info.put(ElFinderConstants.ELFINDER_PARAMETER_DIRECTORY_FILE_NAME, target.getName());
            info.put(ElFinderConstants.ELFINDER_PARAMETER_PARENTHASH, target.getParent().getHash());
        }

        if (target.isFolder())
        {
            info.put(ElFinderConstants.ELFINDER_PARAMETER_HAS_DIR, target.hasChildFolder()
                    ? ElFinderConstants.ELFINDER_TRUE_RESPONSE : ElFinderConstants.ELFINDER_FALSE_RESPONSE);
        }
        return info;
    }

    protected Map<String, Object> getOptions(VolumeHandler cwd)
    {
        String[] emptyArray = {};
        options.put(ElFinderConstants.ELFINDER_PARAMETER_PATH, cwd.getName());
        options.put(ElFinderConstants.ELFINDER_PARAMETER_COMMAND_DISABLED, emptyArray);
        options.put(ElFinderConstants.ELFINDER_PARAMETER_FILE_SEPARATOR,
                ElFinderConstants.ELFINDER_PARAMETER_FILE_SEPARATOR);
        options.put(ElFinderConstants.ELFINDER_PARAMETER_OVERWRITE_FILE, ElFinderConstants.ELFINDER_TRUE_RESPONSE);
        options.put(ElFinderConstants.ELFINDER_PARAMETER_ARCHIVERS, ArchiverOption.JSON_INSTANCE());
        return options;
    }

    class Task implements Runnable
    {

        private HttpServletRequest request;

        private VolumeHandler itemHandler;

        private List<Map<String, Object>> jsonFileList;

        private CountDownLatch downLatch;

        public Task(List<Map<String, Object>> jsonFileList, HttpServletRequest request, VolumeHandler itemHandler,
                CountDownLatch downLatch)
        {
            this.jsonFileList = jsonFileList;
            this.request = request;
            this.itemHandler = itemHandler;
            this.downLatch = downLatch;
        }

        @Override
        public void run()
        {
            try
            {
                jsonFileList.add(getTargetInfo(request, itemHandler));
                downLatch.countDown();
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
    }
}